
#include "gui_form.h"

using namespace WtfGui;
using namespace WtfGui::Forms;
using namespace WtfEngine;


/**** Button ****/

Button::Button(const WtfGui::LayoutContainer::tCreationOptions &opts, int axis)
: LayoutContainer(opts, axis)
{
};

bool Button::OnKeyPress(int key, int flags) {
	if(key == '\r') {
		if(!mrOnPress.isNull()) {
			(*mrOnPress)();
		};
		return true;
	} else {
		return LayoutContainer::OnKeyPress(key, flags);
	};
};

bool Button::OnMouseDown(const iVector2D &vPos, WtfEngine::tMouseButton btn) {
	if(LayoutContainer::OnMouseDown(vPos, btn))
		return true;

	if(btn == MOUSE_LEFT || btn == MOUSE_RIGHT) {
		return true;
	} else {
		return false;
	};
};

bool Button::OnMouseUp(const iVector2D &vPos, WtfEngine::tMouseButton btn) {
	if(LayoutContainer::OnMouseUp(vPos, btn))
		return true;

	if(btn == MOUSE_LEFT) {
		if(!mrOnPress.isNull()) {
			(*mrOnPress)();
		};
	} else if(btn == MOUSE_RIGHT) {
		if(!mrOnRightClick.isNull()) {
			(*mrOnRightClick)();
		};
	} else {
		return false;
	};

	return true;
};

bool Button::OnMouseOver(const iVector2D &vPos, const iVector2D &vDelta) {
	if(!mrOnMouseOver.isNull()) {
		(*mrOnMouseOver)();
	};
	return true;
};

bool Button::OnMouseOut(const iVector2D &vPos, const iVector2D &vDelta) {
	if(!mrOnMouseOut.isNull()) {
		(*mrOnMouseOut)();
	};
	return true;
};


/**** Slider ****/

Slider::Slider(int axis, float fMin, float fMax, float fValue, float fSnap)
: WidgetContainer(""),
mfMin(fMin), mfMax(fMax), mfValue(fValue), mfSnap(fSnap), miAxis(axis),
mbIsSliding(false) {
	mrMinusButton = new Button(Button::tCreationOptions());
	mrPlusButton = new Button(Button::tCreationOptions());
	mrSlider = new Button(Button::tCreationOptions());

	mrMinusButton->setOnMouseDown(new ObjFunctor<Slider>(this, &Slider::OnMinusDown));
	mrPlusButton->setOnMouseDown(new ObjFunctor<Slider>(this, &Slider::OnPlusDown));
	mrSlider->setOnMouseDown(new ObjFunctor<Slider>(this, &Slider::OnSliderDown));

	Widgets::Label::tRef rLabel = new Widgets::Label("-");
	rLabel->ExpandToText();
	mrMinusButton->setInnerSize(rLabel->getSize());
	mrMinusButton->AddChild(rLabel);

	rLabel = new Widgets::Label("+");
	rLabel->ExpandToText();
	mrPlusButton->setInnerSize(rLabel->getSize());
	mrPlusButton->AddChild(rLabel);

	rLabel = new Widgets::Label("o");
	rLabel->ExpandToText();
	mrSlider->setInnerSize(rLabel->getSize());
	mrSlider->AddChild(rLabel);

	// Position plus at end of slider
	AdvancedLayout::LayoutRule::tRef zzrLayout[2][2];
	zzrLayout[0][0] = AdvancedLayout::Default();
	zzrLayout[0][1] = AdvancedLayout::Default();
	zzrLayout[1][0] = AdvancedLayout::Default();
	zzrLayout[1][1] = AdvancedLayout::Default();

	this->AddChild(mrMinusButton);
	this->AddChild(mrPlusButton);
	this->AddChild(mrSlider);

	zzrLayout[0][axis] = new AdvancedLayout::InnerSizeOf(this->getLayout(),
		new AdvancedLayout::Subtract(new AdvancedLayout::SizeOf(mrPlusButton->getLayout())));
	new AdvancedLayout(mrPlusButton->getLayout(),
		zzrLayout[0][0], zzrLayout[0][1], zzrLayout[1][0], zzrLayout[1][1]);
};

void Slider::setValue(float f) {
	mfValue = f;
	UpdateLayout();
};

void Slider::OnMinusDown() {
	setValue(getValue() - ((mfMax - mfMin) / getSize()[miAxis]));
};
void Slider::OnPlusDown() {
	setValue(getValue() + ((mfMax - mfMin) / getSize()[miAxis]));
};
void Slider::OnSliderDown() {
	mbIsSliding = true;
};

bool Slider::OnMouseDown(const iVector2D& vPos, WtfEngine::tMouseButton btn) {
	return WidgetContainer::OnMouseDown(vPos, btn);
};

bool Slider::OnMouseUp(const iVector2D& vPos, WtfEngine::tMouseButton btn) {
	mbIsSliding = false;
	WidgetContainer::OnMouseUp(vPos, btn);
	return true;
};

bool Slider::OnMouseMove(const iVector2D& vPos, const iVector2D& vDelta) {
	if(mbIsSliding) {
		iVector2D vPos = mrSlider->getPosition();
		vPos[miAxis] += vDelta[miAxis];
		mrSlider->Move(vPos);
		UpdateValue();
		return true;
	} else {
		return WidgetContainer::OnMouseMove(vPos, vDelta);
	};
};

void Slider::UpdateLayout()
{
	// Clamp value
	mfValue = mfValue < mfMin ? mfMin : (mfValue > mfMax ? mfMax : mfValue);

	NotifyObservers();

	// Position slider
	iVector2D vPos;
	vPos[miAxis] = int(((mfValue - mfMin) / (mfMax - mfMin)) * float(getInnerSize()[miAxis]));
	mrSlider->getLayout()->setPosition(vPos);
};

void Slider::UpdateValue()
{
	setValue(mfMin + ( float(mrSlider->getPosition()[miAxis]) * (mfMax - mfMin) / float(getInnerSize()[miAxis]) ));
};



/**** Table ****/

Table::Record::Record(const WtfGui::LayoutContainer::tCreationOptions &opts, int axis, const Table *pTable)
: LayoutContainer(opts, axis), mrTable(pTable)
{
	setBackground(tBackground::tCacheRef("v_gui_tableRecordBg"));
};

void Table::Record::UpdateLayout()
{
	iVector2D	vPos, vSize;
	iVector2D	vTableSize = mrTable->getInnerSize();
	int         iOppAxis = 1 - miAxis;
	int			iField = 0;
	
	vSize[iOppAxis] = getSize()[iOppAxis] - (getPadding2D<0>()[iOppAxis] + getPadding2D<1>()[iOppAxis]);

	tChildList::const_iterator i;
	for(i = mChildren.begin(); i != mChildren.end() && iField < mrTable->getNumHeadings(); i++) {
		// Add spacing
		vPos[miAxis] += miElementSpacing;

		// Position element
		vSize[miAxis] = (int)(mrTable->getHeading(iField).fScale * (float)vTableSize[miAxis]);
		vSize[iOppAxis] = maxf(vSize[iOppAxis], (*i)->getSize()[iOppAxis]);
		//(*i)->Move(vPos);
		//(*i)->Resize(vSize);
		vPos[miAxis] += vSize[miAxis];

		iField++;
	};

	vSize[miAxis] = maxf(getSize()[miAxis], vPos[miAxis] + miElementSpacing);
	this->Resize(vSize);

	for(; i != mChildren.end(); i++) {
		// Add spacing
		vPos[miAxis] += miElementSpacing;
		
		// Position element
		(*i)->Move(vPos);
		vPos[miAxis] += (*i)->getSize()[miAxis];
	};
};

void Table::Record::AddChild(const Widget::tRef& rChild)
{
	AdvancedLayout::LayoutRule::tRef vvrLayout[2][2];
	int i = mChildren.size();
	int iOppAxis = 1 - miAxis;

	rChild->Resize(getInnerSize());

	if(i > 0) {
		vvrLayout[0][miAxis] = AdvancedLayout::PositionAfter(mChildren.back()->getLayout())->Then(new AdvancedLayout::Displacement(miElementSpacing));
	} else {
		vvrLayout[0][miAxis] = AdvancedLayout::Default();
	};
	vvrLayout[0][iOppAxis] = AdvancedLayout::Default();
	vvrLayout[1][miAxis] = AdvancedLayout::InnerSizeScale(mrTable->getLayout(), mrTable->getHeading(i).fScale);
	vvrLayout[1][iOppAxis] = AdvancedLayout::Default();

	new AdvancedLayout(rChild->getLayout(), vvrLayout[0][0], vvrLayout[0][1], vvrLayout[1][0], vvrLayout[1][1]);

	WidgetContainer::AddChild(rChild);
	//UpdateLayout();
};

void Table::Record::RemoveChild(const Widget::tRef& rChild)
{
	WidgetContainer::RemoveChild(rChild);
	UpdateLayout();
};

void Table::Record::OnResize() {
	Widget::OnResize();
};

void Table::Record::OnChildResize() {
	//UpdateLayout();
};

void Table::Record::OnParentResize() {
	UpdateLayout();
};

Table::Table(const WtfGui::ScrollLayout::tCreationOptions &opts, int axis)
: ScrollLayout(opts, axis), mrHeader((Record*)&*AddRecord()) {
	mrHeader->setBackground(tBackground::tCacheRef("v_gui_tableHeaderBg"));
};

void Table::AddHeading(const WtfGui::Forms::Table::Heading &heading) {
	mvHeadings.push_back(heading);
	mrHeader->AddChild(heading.rBaseWidget);
};

void Table::setHeading(int i, const Table::Heading& heading) {
	mvHeadings[i].rBaseWidget->Remove();
	mvHeadings[i] = heading;
	mrHeader->AddChild(heading.rBaseWidget);
	UpdateLayout();
};

WidgetContainer::tRef Table::AddRecord() {
	int iOppAxis = 1 - miAxis;
	iVector2D vSize;
	Record::tCreationOptions opts;
	WidgetContainer::tRef rRec = new Record(opts, iOppAxis, this);
	
	vSize[miAxis] = 26;
	vSize[iOppAxis] = getSize()[iOppAxis] - (getPadding2D<0>()[iOppAxis] + getPadding2D<1>()[iOppAxis]);
	rRec->Resize(vSize);

	AdvancedLayout::LayoutRule::tRef vvrLayout[2][2];
	vvrLayout[0][miAxis] = AdvancedLayout::Default();
	vvrLayout[0][iOppAxis] = AdvancedLayout::Default();
	vvrLayout[1][miAxis] = AdvancedLayout::Default();
	vvrLayout[1][iOppAxis] = new AdvancedLayout::InnerSizeOf(this->getLayout());
	new AdvancedLayout(rRec->getLayout(), vvrLayout[0][0], vvrLayout[0][1], vvrLayout[1][0], vvrLayout[1][1]);

	this->AddChild(rRec);
	return rRec;
};
